﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Reflection;

using Akabana.Amf.Util;
using Akabana.Amf.Messaging;
using Akabana.Amf.Messaging.Gateway.Exception;


namespace Akabana.Amf.Messaging.Gateway.Service
{
    /**
     * <summary>リモーティングサービスメソッド実行クラス</summary>
     */
    public class MessagingServiceMethodInvoker
    {
        private const string SERVICE_METHOD_REG = @"^(.+?)\.(.+)$";

        private static MethodInfo FindInvokeMethodInfo(Type serviceType, string methodName, object args)
        {
            MethodInfo methodInfo = MessagingServiceAttributeResolver.FindServiceMethodInfo(serviceType, methodName, (object[])args);

            if (methodInfo == null)
            {
                throw new IllegalServiceMethodException(serviceType.Name + "." + methodName);
            }
            return methodInfo;
        }

        private Dictionary<string, object> serviceInstanceCache;

        /**
         * <summary>コンストラクタ</summary>
         */
        public MessagingServiceMethodInvoker()
        {
            serviceInstanceCache = new Dictionary<string, Object>();
        }

        /**
         * <summary>アセンブリを追加する</summary>
         * <param name="assembly">アセンブリ</param>
         */
        public void AddAssembly(Assembly assembly)
        {
            var types = MessagingServiceAttributeResolver.GetRemotingServices(assembly);
            foreach (var pair in types)
            {
                object service = pair.Value.CreateInstance();
                serviceInstanceCache.Add(pair.Key, service);
            }            
        }

        /**
         * <summary>サービスメソッドを実行する</summary>
         * <param name="serviceMethodName">サービスメソッド名</param>
         * <param name="args">サービスメソッドの引数</param>
         * <returns>実行結果</returns>
         * <exception cref="System.Exception">メソッド実行時</exception>
         * <exception cref="Akabana.Amf.Messaging.Gateway.Exception.IllegalServiceException">不正なサービスが指定された場合</exception>
         */
        public object Invoke(string serviceMethodName, object args)
        {

            var matchesGroups = Regex.Match(serviceMethodName, SERVICE_METHOD_REG).Groups;
            if (matchesGroups.Count == 3)
            {
                var serviceName = matchesGroups[1].Value;
                var methodName = matchesGroups[2].Value;
                if (!(args.GetType().IsArray))
                {
                    args = new object[1] { args };
                }

                return DoServiceMethodInvoker(serviceName, methodName, (object[])args);
            }
            else
            {
                throw new IllegalServiceException(serviceMethodName);
            }
        }

        private object DoServiceMethodInvoker(string serviceName, string methodName, object[] args)
        {
            object service = null;
            lock (this)
            {
                if (serviceInstanceCache.TryGetValue(serviceName, out service))
                {
                }
                else
                {
                    throw new UnRegisteredServiceException(serviceName);
                }
            }
                
            var serviceType = service.GetType();
            var serviceMethodInfo = FindInvokeMethodInfo(serviceType, methodName, args);

            return MethodInfoUtil.Invoke(service, serviceMethodInfo, args);
        }
    }
}
